<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />

    <script>
      // Load the saved width immediately
      const savedWidth = localStorage.getItem("tocWidth") || "178px";
      document.documentElement.style.setProperty("--toc-width", savedWidth);
    </script>

    <link rel="stylesheet" href="highlight.js/styles/github.min.css" />
    <link
      rel="stylesheet"
      href="highlight.js/styles/github-dark.min.css"
      media="(prefers-color-scheme: dark)"
    />

    <script src="highlight.js/highlight.min.js"></script>

    <script src="js/markdownItAnchor.umd.js"></script>
  </head>
  <body class="markdown-body">
    <link rel="stylesheet" href="css/github-markdown.css" />

    <script src="js/markdown-it.min.js"></script>

    <style>
      body {
        background-color: var(--bgColor-default);
        margin: 0;
        padding: 0;
      }

      /* Add a wrapper div for the flex layout */
      .container {
        display: flex;
        min-height: 100vh;
      }

      #preview {
        flex: 1;
        padding: 1rem;
        min-width: 0;
      }

      /* From github.com */
      .container-lg {
        max-width: 1012px;
        margin-right: auto;
        margin-left: auto;
      }

      #toc {
        font-family: "Segoe UI", Helvetica, Arial, sans-serif, "Segoe UI Emoji",
          "Segoe UI Symbol";
        position: sticky;
        top: 0;
        left: 0;

        width: var(--toc-width);

        min-width: 4px;
        max-width: 80%;

        height: 100vh;
        background: var(--bgColor-muted);

        overflow-y: auto;
        overflow-x: hidden;

        max-height: 100vh;
        box-sizing: border-box;
      }

      #toc a {
        display: block;
        text-overflow: ellipsis;
        overflow: hidden;
      }

      .table-of-contents {
        font-size: 0.8em;
        padding: 1em 2em;
      }

      .table-of-contents ul {
        list-style-type: none;
        padding-left: 1em;
      }

      .table-of-contents > ul {
        padding: 0;
      }

      .table-of-contents li {
        margin: 0 0 0.75em 0;
      }

      .table-of-contents a {
        color: var(--fgColor-default);
        text-decoration: none;
        border-left: 2px solid transparent;
        margin-left: -1em;
        padding-left: calc(1em - 2px);
        transition: border-left-color 0.2s ease-out;
      }

      .table-of-contents a:hover {
        color: var(--fgColor-accent);
      }

      .table-of-contents .active {
        color: var(--fgColor-accent);
        border-left: 2px solid var(--fgColor-accent);
        margin-left: -1em;
        padding-left: calc(1em - 2px);
        transition: all 0.2s ease-out;
      }

      h1:target,
      h2:target,
      h3:target,
      h4:target {
        animation: highlight 1s ease;
      }

      @keyframes highlight {
        from {
          background: var(--bgColor-attention-muted);
        }
        to {
          background: var(--bgColor-default);
        }
      }

      .resize-overlay {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 9999;
        cursor: ew-resize;
        user-select: none;
      }
    </style>

    <textarea id="text-input" style="display: none">{{content}}</textarea>

    <div class="container">
      <div id="toc" class="toc"></div>
      <div id="preview" class="container-lg"></div>
    </div>

    <script>
      var md = window
        .markdownit({
          html: true,
          typographer: false,
          quotes: "“”‘’",
          highlight: function (str, lang) {
            if (lang && hljs.getLanguage(lang)) {
              try {
                return (
                  '<pre class="hljs"><code>' +
                  hljs.highlight(str, { language: lang, ignoreIllegals: true })
                    .value +
                  "</code></pre>"
                );
              } catch (__) {}
            }
            return (
              '<pre class="hljs"><code>' +
              md.utils.escapeHtml(str) +
              "</code></pre>"
            );
          },
        })
        .use(markdownItAnchor, {
          permalink: false,
          slugify: (s) =>
            s
              .toLowerCase()
              .replace(/[^a-z0-9 -]/g, "")
              .replace(/\s+/g, "-")
              .replace(/-+/g, "-"),
        });
      document.getElementById("preview").innerHTML = md.render(
        document.getElementById("text-input").value
      );

      /* codes below are adopted from https://codepen.io/jtojnar/full/Juiop */
      var ToC = `<nav role='navigation' class='table-of-contents'>
        <h2>Contents</h2>
        <ul>`;

      var newLine,
        el,
        title,
        link,
        level = 0,
        baseLevel = 0,
        count = 0;

      document
        .querySelectorAll("#preview h1, #preview h2, #preview h3, #preview h4")
        .forEach(function (heading) {
          count++;
          el = heading;
          title = el.textContent;

          if (!el.id) el.id = "h-" + count;
          link = "#" + el.id;

          var prevLevel = level || 0;
          level = heading.nodeName.substr(1);
          if (!baseLevel) {
            // make sure you start with highest level of heading or it won't work
            baseLevel = level;
          }
          if (prevLevel == 0) {
            newLine = "<li>";
          } else if (level == prevLevel) {
            newLine = "</li><li>";
          } else if (level > prevLevel) {
            newLine = "</li><ul><li>";
          } else if (level < prevLevel) {
            newLine = "</li></ul>".repeat(prevLevel - level) + "</li><li>";
          }

          newLine += `<a href="${link}"${
            count === 1 ? ` class="active"` : ``
          }>${title}</a>`;

          ToC += newLine;
        });

      ToC +=
        "</li></ul>".repeat(level - baseLevel) + "</li>" + "</ul>" + "</nav>";

      document.getElementById("toc").innerHTML = ToC;

      if (count < 2) {
        document.getElementById("toc").style.display = "none";
        document.querySelector(".markdown-body").style.width = "100%";
      }
    </script>

    <script>
      // == Resizable TOC width ==
      document.addEventListener("DOMContentLoaded", function () {
        const toc = document.getElementById("toc");
        let isResizing = false;
        let lastDownX = 0;
        let overlay = null;
        const RESIZE_HANDLE_WIDTH = 8; // Width of the resize handle area in pixels

        // Create overlay function
        function createOverlay() {
          overlay = document.createElement("div");
          overlay.className = "resize-overlay";
          document.body.appendChild(overlay);
          document.body.style.userSelect = "none"; // Disable text selection
        }

        // Remove overlay function
        function removeOverlay() {
          if (overlay && overlay.parentNode) {
            overlay.parentNode.removeChild(overlay);
            overlay = null;
            document.body.style.userSelect = ""; // Re-enable text selection
          }
        }

        // Check if cursor is in resize area
        function isInResizeArea(clientX, rect) {
          const toc = document.getElementById("toc");
          const hasScrollbar = toc.scrollHeight > toc.clientHeight;
          const scrollbarWidth = hasScrollbar ? 17 : 0; // Standard scrollbar width in most browsers
          return (
            clientX > rect.right - scrollbarWidth - RESIZE_HANDLE_WIDTH &&
            clientX < rect.right - scrollbarWidth + RESIZE_HANDLE_WIDTH
          );
        }

        // Handle cursor style when moving over the resize area
        toc.addEventListener("mousemove", function (e) {
          const rect = toc.getBoundingClientRect();
          if (isInResizeArea(e.clientX, rect)) {
            toc.style.cursor = "ew-resize";
          } else {
            toc.style.cursor = "auto";
          }
        });

        toc.addEventListener("mousedown", function (e) {
          const rect = toc.getBoundingClientRect();
          if (isInResizeArea(e.clientX, rect)) {
            isResizing = true;
            lastDownX = e.clientX;
            createOverlay();
          }
        });

        document.addEventListener("mousemove", function (e) {
          if (!isResizing) return;
          const width = toc.getBoundingClientRect().width;
          const newWidth = width + (e.clientX - lastDownX);

          toc.style.width = newWidth + "px";

          lastDownX = e.clientX;
          e.preventDefault();
        });

        document.addEventListener("mouseup", function () {
          if (isResizing) {
            isResizing = false;
            removeOverlay();
            // Save the new width
            const newWidth = toc.style.width;
            localStorage.setItem("tocWidth", newWidth);
            document.documentElement.style.setProperty("--toc-width", newWidth);
          }
        });

        // Reset cursor when leaving the TOC
        toc.addEventListener("mouseleave", function () {
          if (!isResizing) {
            toc.style.cursor = "auto";
          }
        });

        // Prevent default behavior of spacebar scrolling the page, while we use spacebar to dismiss the preview
        document.addEventListener("keydown", function (e) {
          if (e.code === "Space") {
            e.preventDefault();
          }
        });
      });
    </script>

    <script>
      // == Help track your position in the file with highlighted TOC link ==
      document.addEventListener("DOMContentLoaded", function () {
        let isManualScroll = false;

        const observer = new IntersectionObserver(
          (entries) => {
            if (isManualScroll) return;
            entries.forEach((entry) => {
              if (entry.isIntersecting) {
                updateActiveHeading(entry.target);
              }
            });
          },
          {
            rootMargin: "-10% 0px -85% 0px", // Adjust these values to change when the active state triggers
          }
        );

        // Function to update active heading
        function updateActiveHeading(heading) {
          document.querySelectorAll(".table-of-contents a").forEach((link) => {
            link.classList.remove("active");
          });

          const id = heading.getAttribute("id");
          const tocLink = document.querySelector(
            `.table-of-contents a[href="#${id}"]`
          );
          if (tocLink) {
            tocLink.classList.add("active");

            //#region Add auto-scroll functionality
            const toc = document.getElementById("toc");
            const tocRect = toc.getBoundingClientRect();
            const linkRect = tocLink.getBoundingClientRect();

            // Check if the active link is outside the visible area
            if (
              linkRect.top < tocRect.top ||
              linkRect.bottom > tocRect.bottom
            ) {
              // Simply scroll the element into view with a small offset
              tocLink.scrollIntoView({
                behavior: "smooth",
                block: "nearest",
              });
            }
            //#endregion
          }
        }

        // Function to find the current heading based on scroll position
        function findCurrentHeading() {
          const headings = Array.from(
            document.querySelectorAll(
              "#preview h1, #preview h2, #preview h3, #preview h4"
            )
          );

          for (const heading of headings) {
            const rect = heading.getBoundingClientRect();
            // Check if the heading is in the viewport
            if (rect.top >= 0 && rect.top <= window.innerHeight * 0.3) {
              return heading;
            }
          }

          // If no heading is found in the viewport, return the last heading before viewport
          for (let i = headings.length - 1; i >= 0; i--) {
            const rect = headings[i].getBoundingClientRect();
            if (rect.top < 0) {
              return headings[i];
            }
          }

          // If no heading is found, return the first heading
          return headings[0];
        }

        // Modify scroll event handler
        document.addEventListener("scroll", () => {
          if (isManualScroll) return;
          const currentHeading = findCurrentHeading();
          if (currentHeading) {
            updateActiveHeading(currentHeading);
          }
        });

        // Modify anchor link click handler
        document.querySelectorAll(".table-of-contents a").forEach((link) => {
          link.addEventListener("click", () => {
            isManualScroll = true;
            const targetId = link.getAttribute("href").substring(1);
            const targetHeading = document.getElementById(targetId);
            if (targetHeading) {
              updateActiveHeading(targetHeading);
            }
            // Reset the flag after a delay
            // We do this to ensure the clicked heading is highlighted, especially when at the bottom of the page
            setTimeout(() => {
              isManualScroll = false;
            }, 10);
          });
        });

        // Observe all headings
        document
          .querySelectorAll(
            "#preview h1, #preview h2, #preview h3, #preview h4"
          )
          .forEach((heading) => {
            observer.observe(heading);
          });

        // Initial active heading
        const currentHeading = findCurrentHeading();
        if (currentHeading) {
          updateActiveHeading(currentHeading);
        }
      });
    </script>

    <script id="MathJax-script" async src="mathjax/tex-mml-svg.js"></script>
  </body>
</html>
